package com.polito.mad.bikeponyapp.activity;

import java.util.ArrayList;
import java.util.List;

import android.app.ActionBar;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.ComponentName;
import android.content.ContentValues;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.graphics.drawable.AnimationDrawable;
import android.os.Bundle;
import android.os.IBinder;
import android.preference.PreferenceManager;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentTransaction;
import android.view.Gravity;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.ImageView;
import android.widget.PopupMenu;
import android.widget.PopupMenu.OnMenuItemClickListener;
import android.widget.Toast;

import com.polito.mad.bikeponyapp.BikePonyApplication;
import com.polito.mad.bikeponyapp.R;
import com.polito.mad.bikeponyapp.fragments.AdditionalInfoFragment;
import com.polito.mad.bikeponyapp.fragments.DeliveryListViewFragment;
import com.polito.mad.bikeponyapp.fragments.DeliveryMapFragment;
import com.polito.mad.bikeponyapp.list.ListViewAdapter.OnShowSingleLocationListener;
import com.polito.mad.bikeponyapp.persistence.DeliveriesRepository;
import com.polito.mad.bikeponyapp.persistence.DeliveryItem;
import com.polito.mad.bikeponyapp.service.ServerCommunicationService;
import com.polito.mad.bikeponyapp.service.ServerCommunicationService.OnProximityListener;
import com.polito.mad.bikeponyapp.service.ServerCommunicationService.OnTasksListener;
import com.polito.mad.bikeponyapp.util.AgentState;
import com.polito.mad.bikeponyapp.util.AsyncTaskForResultMessage;
import com.polito.mad.bikeponyapp.util.DeliveriesFacade;
import com.polito.mad.bikeponyapp.util.DialogCreator;
import com.polito.mad.bikeponyapp.util.ReroutingAsyncTask.OnReroutingListener;
import com.polito.mad.bikeponyapp.util.ResultMessage;

public class MainActivity extends FragmentActivity implements OnShowSingleLocationListener,OnMenuItemClickListener,DeliveriesFacade,OnTasksListener,OnProximityListener{

	private static final String CURRENT_PAGE_PARAM_NAME = "current_page";
	private static final String DELETED_TASK_ALERT_VISIBLE = "deleted_task_alert_visible";
	private static final String NEW_TASK_ALERT_VISIBLE = "new_task_alert_visible";
	public static final int LOGIN_REQUEST_CODE = 0x0011;
	public static final int LOGOUT_REQUEST_CODE = 0x0010;
	public static final int NOTIFICATION_REQUEST_CODE = 0x0012;

	public static final String LOGOUT_ACTION = "logout";
	protected static final int LIST_FRAGMENT_INDEX = 0;
	private static final int MAP_FRAGMENT_INDEX = 1;
	private List<ServiceStateChangeObserver> serviceStateChangeObservers;
	
	private ServerCommunicationService localService;
	private DeliveryListViewFragment deliveryListViewFragment;
	private DeliveryMapFragment deliveryMapFragment;
	private AdditionalInfoFragment additionalInfoFragment;
	private int itemToCenterTo = -1;
	private int currentPageSelected;
	private boolean loginPerformed;
	private boolean restarting;
	private boolean serviceIsBound;
	private AlertDialog currentShowingDialog;
	private DeliveriesRepository repository;
	private boolean isShowingNewTaskAlert;
	private boolean isShowingDeletedTasksAlert;

	
	@Override
	protected void onCreate(Bundle savedInstanceState) 
	{
		super.onCreate(savedInstanceState);
		if(savedInstanceState!=null)
		{
			currentPageSelected = savedInstanceState.getInt(CURRENT_PAGE_PARAM_NAME);
			isShowingNewTaskAlert = savedInstanceState.getBoolean(NEW_TASK_ALERT_VISIBLE);
			isShowingDeletedTasksAlert = savedInstanceState.getBoolean(DELETED_TASK_ALERT_VISIBLE);
			loginPerformed = savedInstanceState.getBoolean(getString(R.string.login_performed));
			//delivery_id = savedInstanceState.getLong(getString(R.string.delivery_id_notification_intent_param));
		}else
		{
			/*if no data have been saved , the MainActivity will show the ListView as its first view*/
			currentPageSelected = LIST_FRAGMENT_INDEX;
			/*check if the use is already logged and the activity has been recreated due to a system event such as device orientation change*/
			int agentStatus = PreferenceManager.getDefaultSharedPreferences(this).getInt(getString(R.string.user_status_pref), -1);
			loginPerformed = (agentStatus==  AgentState.STATUS_LOGGED_IN.getState() || agentStatus== AgentState.STATUS_PENDING.getState());
		}
		serviceStateChangeObservers = new ArrayList<MainActivity.ServiceStateChangeObserver>();
		/*save a reference of the delivery repository to be used to query and update deliveries*/
		this.repository = ((BikePonyApplication)getApplication()).getDeliveriesRepository();
		/* this variable is used to distinguish if the activity has been created for the first time or it has been recreated due to a system event like orientation change*/
		restarting = false;
		
		/*search for new TASKS intent sent by the Service whenever new tasks are found*/
		Intent intent = getIntent();
        String action = intent.getAction();
		if(intent!=null && action!=null)
        {
        	//Saqib: notification acitivty is launched from here and then it will redirect to pickup/delivery activity
        	
        	if(action.equals(ServerCommunicationService.ProximityIntentReceiver.DELIVERY_POINT_REACHED_TASK_NOTIFICATION_ACTION))
        	{
        		showNotificationActivity(this, null);
        	}
        	else if(action.equals(ServerCommunicationService.NEW_TASK_NOTIFICATION_ACTION))
        	{
        		Intent newIntent = new Intent(getApplicationContext(),NewTasksActivity.class);
        		newIntent.setAction(ServerCommunicationService.NEW_TASK_NOTIFICATION_ACTION);
        		newIntent.putExtras(intent);
        		startActivity(newIntent);
        	}
        	else if(action.equals(ServerCommunicationService.DELETED_TASK_NOTIFICATION_ACTION))
        	{
        		isShowingDeletedTasksAlert=true;
        	}
        }
        
        /*it's time to set the content of the activity */
		setContentView(R.layout.main_drawer);
		
		/*check if the current layout is the one supported by large devices in landscape mode :
		 *this kind of devices will have a split View showing all 3 fragments in the same time*/
		/*initialize fragments objects if necessary*/
		if(deliveryListViewFragment==null)
			deliveryListViewFragment = new DeliveryListViewFragment();
		if(deliveryMapFragment==null)
			deliveryMapFragment = new DeliveryMapFragment();
		if(additionalInfoFragment == null)
			additionalInfoFragment = new AdditionalInfoFragment();
		/*show the View which was shown the last time , 
		 * in case of device orientation change or other system events which caused the activity to be recreated*/
			
		/*initialize the Fragments : this involves the registration of any listener and initialization of internal data*/
		initDeliveriesListFragment();
		initDeliveriesMapFragment();
		
		/*customize the Action BAR*/
		initializeActionBar();
		if(currentPageSelected==LIST_FRAGMENT_INDEX)
		{
			showListFragment();
		}
		else
		{
			/*before showing the Map Fragment , possibly a rerouting has to be calculated depending on the status of the backed items list*/
			requestReroutingAndDisplayResults(itemToCenterTo);
		}
	}
	
	private void showDeletedTasksAlert() {
		isShowingDeletedTasksAlert=true;
		
		currentShowingDialog = DialogCreator.createConfirmAlertDialog(this, "Some items have been deleted by the server", "Items Update",new DialogInterface.OnClickListener() {
			
			@Override
			public void onClick(DialogInterface dialog, int which) {
				isShowingDeletedTasksAlert=false;
				invalidateOptionsMenu();
				dialog.dismiss();
			}
		},null);
		
		currentShowingDialog.setOnDismissListener(new DialogInterface.OnDismissListener() {
			
			@Override
			public void onDismiss(DialogInterface dialog) {
				localService.removeDeletedTasksNotification();
				
			}
		});
		currentShowingDialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
			
			@Override
			public void onCancel(DialogInterface dialog) {
				isShowingDeletedTasksAlert=true;
			}
		});
		
		currentShowingDialog.show();
	}
	@Override
	protected void onRestart() {
		super.onRestart();
		/*
		 * since we have to manage when the user comes back from a task :
		 * save the isLoggedPreference in onPause
		 * */
		restarting=true;
	}

	@Override
	protected void onResume() {
		
		if(isShowingNewTaskAlert)
			onNewTasksAvailable();
		else if(isShowingDeletedTasksAlert)
			showDeletedTasksAlert();
		
		super.onResume();
	}
	
	@Override
	protected void onPause() {
		super.onPause();
		if(currentShowingDialog!=null && currentShowingDialog.isShowing())
		{
			currentShowingDialog.cancel();
		}
		
	}
	
	@Override
	public void onAttachFragment(Fragment fragment) {
		super.onAttachFragment(fragment);
		//FIXME why we did this ?
		if(fragment instanceof DeliveryListViewFragment)
			this.deliveryListViewFragment = (DeliveryListViewFragment)fragment;
		else if(fragment instanceof DeliveryMapFragment)
			this.deliveryMapFragment = (DeliveryMapFragment) fragment;
		else if(fragment instanceof AdditionalInfoFragment)
			this.additionalInfoFragment = (AdditionalInfoFragment)fragment;
	}
	
	@Override
	protected void onStart() {
		super.onStart();
		/*the login check is performed by looking of this start has been preceeded by a restart , thus a system event , or is a frashly new start*/
		if(restarting == true && loginPerformed)
        {
			if(serviceStateChangeObservers==null || serviceStateChangeObservers.isEmpty())
				serviceStateChangeObservers = new ArrayList<MainActivity.ServiceStateChangeObserver>();
			if(!serviceIsBound)
			{
				startService(new Intent(getApplicationContext(), ServerCommunicationService.class));
				doBindService();
			}
        }else
        {
	        /*start the LOGIN PROCESS*/
        	
	        login();
        }
	}
	
	@Override
	protected void onStop() {
		super.onStop();
		/*since the activity has to be destroyed , unbind the service even if it will keep on running in background*/
		doUnbindService();
	}
	/**
	 * add the service state change observers to the list of the observeres .
	 * Any observer will be informed if the state of the service changes
	 * @param serviceStateChangeObserver
	 */
	public void addServiceStateChangeObserver(ServiceStateChangeObserver serviceStateChangeObserver)
	{
		if(this.serviceStateChangeObservers==null)
			this.serviceStateChangeObservers = new ArrayList<MainActivity.ServiceStateChangeObserver>();
		this.serviceStateChangeObservers.add(serviceStateChangeObserver);
	}

	@Override
	protected void onSaveInstanceState(Bundle outState) {
		outState.putInt(CURRENT_PAGE_PARAM_NAME, currentPageSelected);
		outState.putBoolean(NEW_TASK_ALERT_VISIBLE, isShowingNewTaskAlert);
		outState.putBoolean(DELETED_TASK_ALERT_VISIBLE, isShowingDeletedTasksAlert);
		outState.putBoolean(getString(R.string.login_performed), loginPerformed);
		super.onSaveInstanceState(outState);
	}
	
	/**
	 * ActionBar initialization and customization
	 */
	private void initializeActionBar() 
	{
		ActionBar actionBar = getActionBar();
		actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD);
		actionBar.setDisplayHomeAsUpEnabled(false);
		actionBar.setDisplayShowTitleEnabled(false);
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		// Inflate the menu; this adds items to the action bar if it is present.
		getMenuInflater().inflate(R.menu.main, menu);
		return true;
	}
	
	@Override
	public boolean onPrepareOptionsMenu(Menu menu) {
		MenuItem item = menu.findItem(R.id.action_agentStatus);
		SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(this);
		int userStatus = pref.getInt(getString(R.string.user_status_pref), -1);
		if(userStatus == AgentState.STATUS_LOGGED_IN.getState())
		{
			item.setIcon(R.drawable.login_icon);
			item.setTitle(getString(R.string.logged_in_menu_icon_title));
			item.setTitleCondensed(getString(R.string.logged_in_menu_icon_title));
		}
		else if(userStatus == AgentState.STATUS_LOGGED_OFF.getState())
		{
			item.setIcon(R.drawable.logoff_icon);
			item.setTitleCondensed(getString(R.string.logged_off_menu_icon_title));
			item.setTitle(getString(R.string.logged_off_menu_icon_title));
		}else if(userStatus == AgentState.STATUS_PENDING.getState())
		{
			item.setIcon(R.drawable.pending_auth_icon);
			item.setTitleCondensed(getString(R.string.pending_tasks_menu_icon_title));
			item.setTitle(getString(R.string.pending_tasks_menu_icon_title));
		}
		MenuItem notifyItem = menu.findItem(R.id.action_notification);
		//Saqib: when notification icon is hidden after feedback is taken, notifyicon is removed hence notify item will be null..
		if(notifyItem != null)
		{
			boolean visible = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()).getBoolean(getString(R.string.notification_set_icon), false);
			long delivery_id_notification_intent_param = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()).getLong(getString(R.string.delivery_id_notification_intent_param), -1);
			if(delivery_id_notification_intent_param!=-1)
			{
				DeliveryItem deliveryItem = repository.get(delivery_id_notification_intent_param);
				if(deliveryItem==null)
				{
					PreferenceManager.getDefaultSharedPreferences(getApplicationContext())
					.edit()
					.putLong(getString(R.string.delivery_id_notification_intent_param),-1)
					.putBoolean(getString(R.string.notification_set_icon), false)
					.commit();
					visible=false;
					
				}
			}
			notifyItem.setVisible(visible);
			View actionView = notifyItem.getActionView();
			ImageView imageView = (ImageView)actionView.findViewById(R.id.animated_icon);
			if(visible)
			{
				actionView.setOnClickListener(new View.OnClickListener() {
					
					@Override
					public void onClick(View v) {
						Bundle extra = new Bundle();
						//extra.putLong(
						//		getString(R.string.delivery_id_notification_intent_param),
						//		delivery_id);
						long delivery_id_notification_intent_param = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()).getLong(getString(R.string.delivery_id_notification_intent_param), -1);
						extra.putLong(getString(R.string.delivery_id_notification_intent_param),delivery_id_notification_intent_param);
				
						Intent i = new Intent(getApplicationContext(), NotificationActivity.class);
						i.putExtras(extra);
						startActivityForResult(i, NOTIFICATION_REQUEST_CODE);
						
					}
				});
				((AnimationDrawable)imageView.getDrawable()).start();
			}else
			{
				((AnimationDrawable)imageView.getDrawable()).stop();
			}
		}
		if(currentPageSelected==LIST_FRAGMENT_INDEX)
		{
			MenuItem listItem = menu.findItem(R.id.action_showList);
			listItem.setIcon(R.drawable.list_menu_item_selected);
			
		}
		else if(currentPageSelected==MAP_FRAGMENT_INDEX)
		{
			MenuItem mapItem = menu.findItem(R.id.action_showMap);
			mapItem.setIcon(R.drawable.list_menu_item_selected);
			mapItem.setIcon(R.drawable.map_menu_item_selected);
			
		}
			return true;
	}
	
	@Override
	public boolean onOptionsItemSelected(MenuItem item) 
	{
		if(item.getItemId()== android.R.id.home){
			Intent profileIntent = new Intent(getApplicationContext(),SettingsActivity.class);
			startActivity(profileIntent);
		}
		if(item.getItemId() == R.id.action_showMap){
			if(deliveryMapFragment==null)
			{
				deliveryMapFragment = new DeliveryMapFragment();
				initDeliveriesMapFragment();
			}
			if(additionalInfoFragment==null)
				additionalInfoFragment = new AdditionalInfoFragment();

			requestReroutingAndDisplayResults(itemToCenterTo);
			
			
		}else if(item.getItemId()==R.id.action_showList){
			if(deliveryListViewFragment==null)
			{
				deliveryListViewFragment = new DeliveryListViewFragment();
				initDeliveriesListFragment();
			}
			
			showListFragment();
			
			itemToCenterTo=-1;
			
		}else if(item.getItemId()==R.id.action_agentStatus){
			showAgentProfilePopupMenu(findViewById(R.id.action_agentStatus));
		}else if(item.getItemId()== R.id.action_notification)
		{
			Bundle extra = new Bundle();
			//extra.putLong(
			//		getString(R.string.delivery_id_notification_intent_param),
			//		delivery_id);
			extra.putLong(getString(R.string.delivery_id_notification_intent_param), 
					PreferenceManager.getDefaultSharedPreferences(this).getLong(getString(R.string.delivery_id_notification_intent_param), -1));
	
			Intent i = new Intent(this, NotificationActivity.class);
			i.putExtras(extra);
			startActivityForResult(i, NOTIFICATION_REQUEST_CODE);
		}else if(item.getItemId() == R.id.action_help)
		{
			Intent i = new Intent(getApplicationContext(),HelpActivity.class);
			startActivity(i);
		}
		
		return super.onOptionsItemSelected(item);
	}

	/**
	 * When the profile MenuItem is clicked , a submenu under the form of a popup menu id displayed below the selected item
	 * @param v the view under which to show the popup menu
	 */
	public void showAgentProfilePopupMenu(View v) {
		
	    PopupMenu popup = new PopupMenu(this, v);
	    MenuInflater inflater = popup.getMenuInflater();
	    inflater.inflate(R.menu._login_logoff, popup.getMenu());
	    SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(this);
	    final int userStatus = pref.getInt(getString(R.string.user_status_pref), -1);
	    /*
	     * depending on whether the user is logged In or not , show the appropriate menu
	     * */
	    MenuItem changeUserStatusMenuItem = popup.getMenu().findItem(R.id.action_change_user_status);
	    MenuItem currentStatusMenuItem = popup.getMenu().findItem(R.id.current_status);
	    if(userStatus == AgentState.STATUS_LOGGED_IN.getState())
	    {
	    	currentStatusMenuItem.setTitle(getString(R.string.user_logged_in_status_title));
	    	currentStatusMenuItem.setTitleCondensed(getString(R.string.user_logged_in_status_title));
	    	
	    	String logoffString = getString(R.string.profile_menu_logoff);
			changeUserStatusMenuItem.setTitle(logoffString);
	    	changeUserStatusMenuItem.setTitleCondensed(logoffString);
	    }else if(userStatus == AgentState.STATUS_LOGGED_OFF.getState())
	    {
	    	currentStatusMenuItem.setTitle(getString(R.string.user_logged_off_status_title));
	    	currentStatusMenuItem.setTitleCondensed(getString(R.string.user_logged_off_status_title));
	    	
	    	String loginString = getString(R.string.profile_menu_login);
			changeUserStatusMenuItem.setTitle(loginString);
	    	changeUserStatusMenuItem.setTitleCondensed(loginString);
	    }else
	    {
	    	currentStatusMenuItem.setTitle(getString(R.string.user_logoff_pending_status_title));
	    	currentStatusMenuItem.setTitleCondensed(getString(R.string.user_logoff_pending_status_title));
	    	
	    	String loginString = getString(R.string.profile_menu_login);
			changeUserStatusMenuItem.setTitle(loginString);
	    	changeUserStatusMenuItem.setTitleCondensed(loginString);
	    	/* the status is in WANT TO LOGOFF BUT I HAVE PENDING TASKS*/ 
	    	//popup.getMenu().removeItem(changeUserStatusMenuItem.getItemId());
	    }
	    popup.setOnMenuItemClickListener(this);
	    popup.show();
	}
	
	public void login()
	{
		if(loginPerformed)
		{
			if(!serviceIsBound)
			{
				startService(new Intent(getApplicationContext(), ServerCommunicationService.class));
				doBindService();
			}
			return;
		}
		
		/*starts the login activity*/
		Intent loginIntent = new Intent(getApplicationContext(),LoginActivity.class);
		loginIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
		startActivity(loginIntent);
		return;
	}
	
	public void logoff()
	{
		StringBuilder b = new StringBuilder();
		b.append(getString(R.string.server_com_protocol))
		.append("://")
		.append(getString(R.string.server_host))
		.append(":")
		.append(getString(R.string.server_port))
		.append(getString(R.string.server_services_context_path))
		.append(getString(R.string.server_services_agent_path))
		.append(getString(R.string.server_services_agent_logoff));
		AsyncTaskForResultMessage asyncTask = new AsyncTaskForResultMessage(b.toString(), false, new AsyncTaskForResultMessage.OnTaskCompletionListener() {
			
			@Override
			public void onTaskEnded(ResultMessage resultMessage) {
				MainActivity.this.onLogoff(resultMessage);
				
			}
		});
		ContentValues cv = new ContentValues();
		String agentId = PreferenceManager.getDefaultSharedPreferences(this).getString(getText(R.string.userid_pref).toString(), "");
		cv.put("agentId", agentId);
		asyncTask.execute(cv);
	}

	/**
	 * Utility method to handle the login response received from the server
	 * @param resultMessage
	 * @return
	 */
	public boolean onLogin(ResultMessage resultMessage)
	{
		String toastmessage=null;
		switch(resultMessage.getStatusCode())
		{
			case ResultMessage.STATUS_OK:
				/*update the UI agent icon status to logged in */
				loginPerformed = true;
				toastmessage = getString(R.string.login_succesfull_message);
				setAgentStatusPref(AgentState.STATUS_LOGGED_IN);
				
				/*START requesting location updates*/
				
				startService(new Intent(getApplicationContext(), ServerCommunicationService.class));
				doBindService();
				
				return true;
				/*remove the LOGOUT button from the drawer panel and from any other menus*/
			case ResultMessage.STATUS_ERR_ALREADY_LOGGED_IN:
				loginPerformed = true;
				toastmessage = getResources().getString(R.string.login_failed_format, resultMessage.getMessage());
				//toastmessage = "Login failed : "+resultMessage.getMessage();
				setAgentStatusPref(AgentState.STATUS_LOGGED_IN);
				
				/* Actually in this case , when the user is already logged in , the Service should have been already started.
				 * We simply bind to it */
				doBindService();
				
			break;
			case ResultMessage.STATUS_ERR_UNKNOWN:
				toastmessage = getResources().getString(R.string.login_failed_format, resultMessage.getMessage());
			break;
			default:
				toastmessage="";
			break;
		}
		SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(this);
		pref.edit().putBoolean(getString(R.string.login_performed), true).commit();
		Toast toast = Toast.makeText(MainActivity.this, toastmessage, Toast.LENGTH_LONG);
		toast.setGravity(Gravity.CENTER_VERTICAL| Gravity.CENTER_HORIZONTAL, 0, 0);
		toast.show();
		return false;
	}
	
	/**
	 * Utility method to handle the logoff response received from the server
	 * @param resultMessage
	 * @return
	 */
	public boolean onLogoff(ResultMessage resultMessage)
	{
		String toastMessage=null;
		switch(resultMessage.getStatusCode())
		{
			case ResultMessage.STATUS_OK:
				/*update the UI agent icon status to logged off */
				toastMessage=getString(R.string.logoff_succesfull_message);
				setAgentStatusPref(AgentState.STATUS_LOGGED_OFF);
				loginPerformed=false;
				//localService.stopRequestingLocationUpdates();
				
				/* try to invalidate the oprion menu*/
				invalidateOptionsMenu();
				
				/*
				 * stop the service if not yet STOPPED , we can avoid consuming energy, 
				 * because we don't need the server communication
				 */
				doUnbindService();
				stopService(new Intent(getApplicationContext(),ServerCommunicationService.class));
				
				/*
				Intent loginIntent = new Intent(getApplicationContext(),LoginActivity.class);
				loginIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK| Intent.FLAG_ACTIVITY_NEW_TASK);
				startActivity(loginIntent);
				*/
				
				break;
			case ResultMessage.STATUS_ERR_ALREADY_LOGGED_OFF:
				/* agent already logged off*/
				toastMessage=getString(R.string.logoff_failed_already_loggedoff_error_message);
				/*in this case we should have the service which is already stopped , but we can retry to stop it*/
				
				doUnbindService();
				stopService(new Intent(getApplicationContext(),ServerCommunicationService.class));
				
				break;
			case ResultMessage.STATUS_ERR_PENDING_TASKS:
				
				setAgentStatusPref(AgentState.STATUS_PENDING);
				toastMessage=getString(R.string.logoff_failed_pending_tasks_error_message);
				invalidateOptionsMenu();
				/* agent cannot perform logoff until pending tasks are completed*/
				break;
			case ResultMessage.STATUS_ERR_UNKNOWN:
				toastMessage=getString(R.string.logoff_failed_communication_error_message);
				/* communication error*/
				break;
		}
		Toast toast = Toast.makeText(MainActivity.this, toastMessage, Toast.LENGTH_LONG);
		toast.setGravity(Gravity.CENTER_VERTICAL| Gravity.CENTER_HORIZONTAL, 0, 0);
		toast.show();
		return false;
	}

	/**
	 * save the user status change in the shared preferences
	 * @param state
	 */
	private void setAgentStatusPref(AgentState state) {
		SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(this);
		Editor editor = pref.edit();
		editor.putInt(getText(R.string.user_status_pref).toString(), state.getState());
		editor.apply();
	}
	
	/**
	 * this callback is invoked whenever new items are received from the serverr , display a Dialog to inform the agent
	 */
	public void onNewTasksAvailable() {
		AlertDialog.Builder builder = new AlertDialog.Builder(this);
		currentShowingDialog = builder.create();
		currentShowingDialog.setTitle(getString(R.string.new_tasks_alert_title));
		currentShowingDialog.setIcon(android.R.drawable.ic_dialog_info);
		currentShowingDialog.setMessage(getString(R.string.new_tasks_alert_content));
		currentShowingDialog.setOnDismissListener(new DialogInterface.OnDismissListener() {
			
			@Override
			public void onDismiss(DialogInterface dialog) {
				
				localService.removeNewTasksNotification();
			}
		});
		currentShowingDialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
			
			@Override
			public void onCancel(DialogInterface dialog) {
				isShowingNewTaskAlert=true;
				
			}
		});
		currentShowingDialog.setButton(Dialog.BUTTON_POSITIVE, getString(android.R.string.ok),new Dialog.OnClickListener() {
			
			@Override
			public void onClick(DialogInterface dialog, int which) {
				/*shows the new items with different colors and start the wobbling of them*/
				if(currentPageSelected!= LIST_FRAGMENT_INDEX)
				{
					showListFragment();
					
					currentPageSelected= LIST_FRAGMENT_INDEX;
				}
				deliveryListViewFragment.startListEditMode();
				isShowingNewTaskAlert=false;
				dialog.dismiss();
			}
		});
		currentShowingDialog.setButton(Dialog.BUTTON_NEGATIVE, getString(android.R.string.no), new Dialog.OnClickListener() {
			
			@Override
			public void onClick(DialogInterface dialog, int which) {
				isShowingNewTaskAlert=false;
				dialog.dismiss();
			}
		});
		
		/*
		 * TODO : ask the user if she wants to optimize the routing : the New added List Items are put by default at  the tail of the list.
		 * Optimization means : developing an algorithm like the TSP ( travel salesman problem ) with constraints: 
		 * 		calculate the optimal route and impose that a pickup cannot stay after a delivery
		 * 		option 1 : use the nierest neighbour algorithm( suboptimal )
		 * 		option 2 : use the simulated annealing --> metaheuristic
		 * 		option 3 : use the genetic algorithms  --> metaheuristic
		 * 		option 4 : use the BEAM search ( not suggested for the kind of problem)
		 * 		option 5 : use the TABU SEARCH 
		 */
		
		currentShowingDialog.show();
		isShowingNewTaskAlert=true;
		
	}
	
	
	@Override
	public void onDeletedTasks() {
		if(currentShowingDialog==null || !currentShowingDialog.isShowing())
		{
			showDeletedTasksAlert();
		}else
		{
			//TODO manage the case that another dialog is showing . We should not forget about this notification
			/*
			 * There can be different approached to the problem : 
			 * 1) check if the deleted items are part of the one which have been declared has the new tasks in the current active dialog
			 * 2) show this dialog below the current dialog and when the current dialog is dismissed , this dialog will appear on top
			 * 
			 * */
		}
		
	}
	private void showMapFragment()
	{
		findViewById(R.id.topFragmentContainer).setVisibility(View.VISIBLE);
		getSupportFragmentManager().beginTransaction()
		.replace(R.id.mainFragmentContainer, deliveryMapFragment)
		.replace(R.id.topFragmentContainer, additionalInfoFragment,"additionalInfo")
		.addToBackStack("showMap").commit();
		currentPageSelected = MAP_FRAGMENT_INDEX;
		invalidateOptionsMenu();
	}
	
	@Override
	public void onBackPressed() {
		if(currentPageSelected== MAP_FRAGMENT_INDEX)
		{
			showListFragment();
		}else
			super.onBackPressed();
	}
	private void showListFragment() 
	{
		
		FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction().replace(R.id.mainFragmentContainer, deliveryListViewFragment);
		Fragment addInfoFragment = getSupportFragmentManager().findFragmentByTag("additionalInfo");
		if(addInfoFragment!=null)
			fragmentTransaction.remove(addInfoFragment);
		fragmentTransaction.commit();
		findViewById(R.id.topFragmentContainer).setVisibility(View.GONE);
		currentPageSelected = LIST_FRAGMENT_INDEX;
		invalidateOptionsMenu();
	}
	/**
	 * the serviceConnection is used to know when the service changes its state i n order to start the meaningful operations inside the application
	 */
	private ServiceConnection mConnection = new ServiceConnection() {
		
		/**
		 * When the service is disconnected for some reason , we should notify all those observers
		 * The MainActivity in this way acts as a Dispatcher
		 */
		@Override
		public void onServiceDisconnected(ComponentName name) {
			localService=null;
			for(ServiceStateChangeObserver observer :serviceStateChangeObservers)
				observer.serviceDisconnected();
		}
		/**
		 * When the service is connected , we bound it taking a reference and we notify all observers
		 * The MainActivity in this way acts as a Dispatcher.
		 * Only when the service is attached , so it is able to communicate to the server, we can start the login procedure
		 */
		@Override
		public void onServiceConnected(ComponentName name, IBinder service) {
				
			localService= ((ServerCommunicationService.LocalBinder)service).getService();
			localService.startRequestingLocationUpdates();
			localService.setOnTasksListener(MainActivity.this);
			if(localService.getOnProximityListener() == null)
			{
				localService.setOnProximityListener(MainActivity.this);
			}
			
			
			for(ServiceStateChangeObserver observer : serviceStateChangeObservers)
			{
				observer.serviceBound(localService);
			}
			
		}
	};
	
	/**
	 * Perform the binding with the service
	 */
	private void doBindService()
	{
		serviceIsBound = bindService(new Intent(getApplicationContext(),ServerCommunicationService.class), mConnection, Context.BIND_AUTO_CREATE);
	}
	
	/**
	 * perform the unbinding with the server
	 */
	private void doUnbindService()
	{
		System.out.println("unbounding the service");
		if(serviceIsBound && localService!=null)
		{
			localService.setOnDestinationValidationListener(null);
			localService.setOnTasksListener(null);
			//localService.setOnProximityListener(null);//blocking it as when activity is not visible it goes the service is unbounded state but we need to listen the proximity notification which is still alive.
			// detach our existing connection
			unbindService(mConnection);
			serviceIsBound=false;
		}
	}
	
	@Override
	protected void onDestroy() {
		super.onDestroy();
		
		deliveryListViewFragment.stopRerouting();
		deliveryListViewFragment=null;
		deliveryMapFragment=null;
		additionalInfoFragment=null;
		
	}

	/**
	 * all the ServiceStateChangeObservers will get notified when the service status change.
	 * @author nicola
	 *
	 */
	public interface ServiceStateChangeObserver
	{
		public void serviceBound(ServerCommunicationService service);
		public void serviceDisconnected();
	}
	
	/**
	 * Interface used by all components to manage the results of Login-logoff procedures
	 * @author nicola
	 *
	 */
	public interface OnLoginLogoffListener
	{
		public void onLogin(ResultMessage resultMessage);
		public void onLogOff(ResultMessage resultMessage);
	}
	
	/**
	 * Utility class which avoid having to override each method of the interface even when just one is needed
	 * @author nicola
	 *
	 */
	public class SimpleOnLoginLogOffListener implements OnLoginLogoffListener
	{
		@Override
		public void onLogin(ResultMessage resultMessage) {
			// MOCK IMPLEMENTATION
		}

		@Override
		public void onLogOff(ResultMessage resultMessage) {
			// MOCK IMPLEMENTATION
		}
	}
	
	private void initDeliveriesListFragment() {
		deliveryListViewFragment.setOnShowSingleLocationListener(MainActivity.this);
		addServiceStateChangeObserver(deliveryListViewFragment);
	}

	private void initDeliveriesMapFragment()
	{
		addServiceStateChangeObserver(deliveryMapFragment);
	}
	
	
	/**
	 * Whenever an item is selected in the ListViewFragment , a calbback method is invoked in order to start the task 
	 * of whowing that item center on the map.
	 * This operation involves a possible recomputation of the trip and the Mapfragment will be inserted in place of the list fragment
	 */
	@Override
	public void showSingleItem(final int itemIndex) {
		this.itemToCenterTo = itemIndex;
		requestReroutingAndDisplayResults(itemToCenterTo);
	}
	
	@Override
	protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
		if(requestCode== LOGIN_REQUEST_CODE)
		{
			switch (resultCode) {
				case Activity.RESULT_OK:
					finishActivity(LOGIN_REQUEST_CODE);
					PreferenceManager.getDefaultSharedPreferences(this)
						.edit()
						.putBoolean(getString(R.string.login_performed), true)
						.commit();
					loginPerformed=true;
					break;
				case ResultMessage.STATUS_ERR_ALREADY_LOGGED_IN:
					finishActivity(LOGIN_REQUEST_CODE);
					PreferenceManager.getDefaultSharedPreferences(this)
						.edit()
						.putBoolean(getString(R.string.login_performed), true)
						.commit();
					loginPerformed= true;
					Toast.makeText(this, "you are already logged in", Toast.LENGTH_SHORT).show();
					break;
				default:
					break;
			}
		}
		else if(requestCode == NOTIFICATION_REQUEST_CODE)
		{
			switch(resultCode)
			{
			case Activity.RESULT_OK:
				break;
			case Activity.RESULT_CANCELED:
				break;
				
			
			}
			
			
		}
		invalidateOptionsMenu();
	}
	/**
	 * calculate the new trip time and links if the backed items list is changed since the last time a trip has been calculated
	 * @param itemIndexToCenterTo
	 */
	private void requestReroutingAndDisplayResults(final int itemIndexToCenterTo)
	{
		if(deliveryListViewFragment.isDirtyList())
		{
			deliveryListViewFragment.startRerouting(this,new OnReroutingListener() {
				
				@Override
				public void onReroutingStarted() {
					
				}
				
				@Override
				public void onReroutingCompleted(boolean success, String message,List<DeliveryItem> deliveryItems) {
					if(success)
						showMapFragment();
					else
						Toast.makeText(getApplicationContext(), getString(R.string.rerouting_completion_error), Toast.LENGTH_SHORT).show();
				}
			});
		}else
			showMapFragment();
	}
	
	/**
	 * Used to respond to clicks on the drop-down menu appearing under the profile Menu icon
	 */
	@Override
	public boolean onMenuItemClick(MenuItem item) {
		
		if(item.getItemId()==R.id.action_change_user_status){
			SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(this);
			int userStatus = pref.getInt(getString(R.string.user_status_pref), -1);
			if(userStatus== AgentState.STATUS_LOGGED_IN.getState())
			{
				 // perform logoff and after prepare the login menu in action bar
				logoff();
			}else if(userStatus == AgentState.STATUS_LOGGED_OFF.getState() || userStatus == AgentState.STATUS_PENDING.getState())
			{
				 //perform login
				Intent intent = new Intent(getApplicationContext(),LoginActivity.class);
				startActivityForResult(intent, LOGIN_REQUEST_CODE);
			}
		}else if(item.getItemId()== R.id.action_edit_profile){
			// show the edit profile settings activity
			Intent settingsIntent = new Intent(getApplicationContext(),SettingsActivity.class);
			startActivity(settingsIntent);
		}
		return false;
	}
	
	@Override
	public List<DeliveryItem> getItems() {
		return repository.getSessionCachedItems();
	}
	
	@Override
	public boolean isDirty() {
		return deliveryListViewFragment.isPathChanged();
	}
	
	@Override
	public int getItemSelected() {
		return itemToCenterTo;
	}
	
	@Override
	public void onNearItemPoint(DeliveryItem item) {
	
		boolean visible = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()).getBoolean(getString(R.string.notification_set_icon), false);
		
		if(!visible)
		{
		
			PreferenceManager.getDefaultSharedPreferences(this).edit()
				.putBoolean(getString(R.string.notification_set_icon), true)
				.commit();
			invalidateOptionsMenu();
			//delivery_id = item.getDeliveryId();
			
			PreferenceManager.getDefaultSharedPreferences(getApplicationContext()).edit()
				.putLong(getString(R.string.delivery_id_notification_intent_param), item.getId())
				.commit();
			
			Bundle extra = new Bundle();
			extra.putLong(getString(R.string.delivery_id_notification_intent_param), item.getId());
			
			showNotificationActivity(this, extra);
		}
		
	}
	
	@Override
	public void onFarFromItemPoint(DeliveryItem item) {
		
		/*
		 * saqib: we should not remove the notification icon until the pickup/delivery code is verified, logically its not correct as
		 * agent has already decided which path to follow when he was given the option to edit the list. now if the delivery point is
		 * reached he should enter the code.
		PreferenceManager.getDefaultSharedPreferences(this).edit()
		.putBoolean(getString(R.string.notification_set_icon), false)
		.commit();
		invalidateOptionsMenu();
		//delivery_id = item.getDeliveryId();
		 
		
		PreferenceManager.getDefaultSharedPreferences(getApplicationContext()).edit()
		.remove(getString(R.string.delivery_id_notification_intent_param))
		.commit();
		 */
	}
	
	private void showNotificationActivity(Context context, Bundle extras)
	{
		if(extras != null)
		{
			Intent newIntent = new Intent(context,NotificationActivity.class);
			//newIntent.setAction(ServerCommunicationService.NEW_TASK_NOTIFICATION_ACTION);
			newIntent.putExtras(extras);
			startActivityForResult(newIntent,NOTIFICATION_REQUEST_CODE);
		}
	}

	@Override
	public void onItemSelected(Integer selectedItemMarker) {
		
		additionalInfoFragment.updateShownItemDetails(selectedItemMarker);
		
	}
	
}
